﻿/*
 * ********************************
 *  Copyright © 2009 - 2010. CoderForRent,LLC. All Rights Reserved.  Licensed under the GNU General Public License version 2 (GPLv2) .
 * 
 * */


using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using CoderForRent.Silverlight.Charting.Core;
using System.Windows.Media;

namespace CoderForRent.Silverlight.Charting.Gantt
{


    /// <summary>
    /// This control will display the gantt nodes' date ranges in a graphical layout.
    /// </summary>
    [
    TemplatePart(Name = "RowPresenter", Type = typeof(GanttRowsPresenter)),
    TemplatePart(Name = "ColumnPresenter", Type = typeof(GanttPanelColumnsPresenter)),
    TemplatePart(Name = "DependenciesPresenter", Type = typeof(GanttDependenciesPresenter)),
    TemplatePart(Name = "MainElement", Type = typeof(FrameworkElement))
    ]
    public class GanttPanel : ContentControl
    {
        #region Dependency Properties
        public static DependencyProperty ToolTipContentTemplateProperty = DependencyProperty.Register("ToolTipContentTemplate", typeof(DataTemplate), typeof(GanttPanel), new PropertyMetadata(null));
        public static DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(GanttPanel), new PropertyMetadata(false));
        public static DependencyProperty RowHeightProperty = DependencyProperty.Register("RowHeight", typeof(double), typeof(GanttPanel), new PropertyMetadata(20d));
        public static DependencyProperty GapBackgroundBrushProperty = DependencyProperty.Register("GapBackgroundBrush", typeof(Brush), typeof(GanttPanel), new PropertyMetadata(new SolidColorBrush(Colors.Transparent)));
        public static DependencyProperty GapBorderBrushProperty = DependencyProperty.Register("GapBorderBrush", typeof(Brush), typeof(GanttPanel), new PropertyMetadata(new SolidColorBrush(Colors.Transparent)));

        public Brush GapBorderBrush
        {
            get { return (Brush)GetValue(GapBorderBrushProperty); }
            set { SetValue(GapBorderBrushProperty, value); }
        }
        public Brush GapBackgroundBrush
        {
            get { return (Brush)GetValue(GapBackgroundBrushProperty); }
            set { SetValue(GapBackgroundBrushProperty, value); }
        }

        /// <summary>
        /// Defines the Content Template that is shown in the tooltip for the displayed GanttItems
        /// </summary>
        public DataTemplate ToolTipContentTemplate
        {
            get { return (DataTemplate)GetValue(ToolTipContentTemplateProperty); }
            set { SetValue(ToolTipContentTemplateProperty, value); }
        }
        /// <summary>
        /// Sets the Height of each of the GanttRows
        /// </summary>
        public double RowHeight { get { return (double)GetValue(RowHeightProperty); } set { SetValue(RowHeightProperty, value); } }
        /// <summary>
        /// Determines whether the items can be moved.
        /// </summary>
        public bool IsReadOnly { get { return (bool)GetValue(IsReadOnlyProperty); } set { SetValue(IsReadOnlyProperty, value); } }

        #endregion

        #region Events
        public event EventHandler<GanttItemEventArgs> ItemChanging;
        protected internal void RaiseItemChanging(GanttItemEventArgs e)
        {
            if (ItemChanging != null)
                ItemChanging(this, e);
        }


        public event EventHandler<GanttItemEventArgs> ItemChanged;
        protected internal void RaiseItemChanged(GanttItemEventArgs e)
        {
            if (ItemChanged != null)
                ItemChanged(this, e);

            UpdateDependencies(e.Item);
        }




        #endregion

        #region Properties
        /// <summary>
        /// This is the first DateTime represented in the panel.
        /// </summary>
        private DateTime _CurrentTime;
        public DateTime CurrentTime
        {
            get { return _CurrentTime; }
            set
            {

                _CurrentTime = value;
                InvalidateItemPositions(); 
            
            }
        }

        internal bool RowsValid { get; set; }

        private int _TopNodeIndex = 0;
        internal int TopNodeIndex
        {
            get { return _TopNodeIndex; }
            set
            {
                if (_TopNodeIndex != value)
                {
                    _TopNodeIndex = value;
                    ResetRowNodeProperties();
                    UpdateDependencies();
                }
            }
        }
        internal int RowCount
        {
            get
            {
                if (RowPresenter == null)
                    return 0;

                return this.RowPresenter.Children.Count;
            }
        }

        private ObservableCollection<IGanttNode> _Nodes;
        public ObservableCollection<IGanttNode> Nodes
        {
            get
            {
                return _Nodes;
            }
            set
            {
                _Nodes = value;
                _Nodes.CollectionChanged += Nodes_CollectionChanged;
                ResetRowNodeProperties();
                UpdateDependencies();
            }
        }

        private ObservableCollection<GanttDependency> _Dependencies;
        public ObservableCollection<GanttDependency> Dependencies
        {
            get
            {
                return _Dependencies;
            }
            set
            {
                if (_Dependencies != value)
                {
                    _Dependencies = value;
                    UpdateDependencies();
                }
            }
        }

        internal GanttChart ParentGanttChart { get; set; }
        #endregion

        #region Public Handlers

        internal double ConvertDateToPosition(DateTime date)
        {
            return TimeUnitScalar.GetPosition(CurrentTime, date);
        }
        internal TimeSpan ConvertDistanceToTimeSpan(double distance)
        {
            return TimeUnitScalar.GetTimespan(CurrentTime, distance);
        }
        #endregion

        #region Template Parts
        protected internal GanttRowsPresenter RowPresenter { get; private set; }
        protected internal GanttPanelColumnsPresenter ColumnPresenter { get; private set; }
        protected internal GanttDependenciesPresenter DependencyPresenter { get; private set; }
        protected internal FrameworkElement MainElement { get; private set; }
        #endregion

        #region Constructors and overrides
        public GanttPanel()
        {
            this.DefaultStyleKey = this.GetType();
            UseLayoutRounding = false;
            this.SizeChanged += new SizeChangedEventHandler(GanttPanel_SizeChanged);
        }


        void GanttPanel_SizeChanged(object sender, SizeChangedEventArgs e)
        {
             ValidateRowCount();
           
        }
        
        protected override Size ArrangeOverride(Size finalSize)
        {
            GenerateRows(finalSize);
            ResetRowNodeProperties();

           // Rect r = new Rect(new Point(0, 0), finalSize);

           // DependencyPresenter.Arrange(r);
            //RowPresenter.Arrange(r);
            //ColumnPresenter.Arrange(r);

            RectangleGeometry r = new RectangleGeometry();
            r.Rect = new Rect(0, 0, finalSize.Width - BorderThickness.Left - BorderThickness.Right, finalSize.Height - BorderThickness.Top - BorderThickness.Bottom);
            MainElement.Clip = r;

            Size result = base.ArrangeOverride(finalSize);
           


            return result;

        }
        protected override Size MeasureOverride(Size availableSize)
        {
            Size result = base.MeasureOverride(availableSize);
            
           // DependencyPresenter.Measure(result);
           // ColumnPresenter.Measure(result);
           // RowPresenter.Measure(result);

            return result;
        }
        
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            RowPresenter = (GanttRowsPresenter)GetTemplateChild("RowPresenter");
            RowPresenter.ParentPanel = this;

            ColumnPresenter = (GanttPanelColumnsPresenter)GetTemplateChild("ColumnPresenter");
            ColumnPresenter.ParentPanel = this;

            DependencyPresenter = (GanttDependenciesPresenter)GetTemplateChild("DependenciesPresenter");
            DependencyPresenter.ParentPanel = this;

            MainElement = (FrameworkElement)GetTemplateChild("MainElement");


        }
        #endregion

        #region Public functions
        public void InvalidateItemPositions()
        {

            if (RowPresenter != null)
                RowPresenter.Children.OfType<GanttRow>().ToList().ForEach(r =>
                {
                    if (r.ItemsPresenter != null)
                    {
                        r.ItemsPresenter.InvalidateMeasure();
                        r.ItemsPresenter.InvalidateArrange();
                    }
                }
                );

            if (ColumnPresenter != null)
                ColumnPresenter.Invalidate();

            if (DependencyPresenter != null)
                DependencyPresenter.Invalidate();
        }
        public void ValidateRowCount()
        {
            int desiredRowCount = (int)Math.Ceiling(this.ActualHeight / this.RowHeight);

            if (RowCount != desiredRowCount)
            {
                GenerateRows(new Size(this.ActualWidth, this.ActualHeight));
                ResetRowNodeProperties();
            }
        }
        #endregion

        #region Private functions
        private void GenerateRows(Size finalSize)
        {
            if (!RowsValid)
            {
                if (RowPresenter == null)
                    return;

                RowsValid = true;

                RowPresenter.Children.Clear();

                double height = finalSize.Height;


                for (double i = 0; i < height; i += RowHeight)
                {
                    GanttRow row = new GanttRow
                    {
                        ParentPanel = this,
                        BorderBrush = this.ParentGanttChart.GanttRowBorderBrush,
                        GapBackgroundBrush = this.GapBackgroundBrush,
                        GapBorderBrush = this.GapBorderBrush
                    };

                    this.RowPresenter.Children.Add(row);
                }

                ResetRowNodeProperties();

            }
        }
        private void ResetRowNodeProperties()
        {
         
            if (RowPresenter == null)
                return;

            for (int i = 0; i < RowPresenter.Children.Count; i++)
            {
                GanttRow row = RowPresenter.Children[i] as GanttRow;
                row.BorderBrush = ParentGanttChart.GanttRowBorderBrush;
                row.Background = Background;

                if (i + TopNodeIndex < Nodes.Count)
                {
                   row.Node = Nodes[i + TopNodeIndex];
                }
                else
                    row.Node = null;
            }
        }

        private GanttRow CreateRow(IGanttNode node)
        {
            GanttRow row = new GanttRow();
            row.BorderBrush = BorderBrush;
            row.Background = Background;
            row.ParentPanel = this;
            row.Node = node;
            return row;
        }
        private GanttRow CreateRow()
        {
            GanttRow row = new GanttRow();
            row.BorderBrush = BorderBrush;
            row.Background = Background;
            row.ParentPanel = this;
            return row;
        }
        private void UpdateDependencies()
        {
            if (DependencyPresenter != null)
                DependencyPresenter.Invalidate();
        }
        private void UpdateDependencies(GanttItem ganttItem)
        {
            var deps = Dependencies.Where(d => d.ChildNode == ganttItem.Node || d.ParentNode == ganttItem.Node);

            var items = DependencyPresenter.Children.Where(ui => (ui as GanttDependencyItem).Dependency.ChildNode == ganttItem.Node || (ui as GanttDependencyItem).Dependency.ParentNode == ganttItem.Node);

            foreach (GanttDependencyItem ui in items)
            {
                ui.UpdateDependencyLines();
            }

        }

        #endregion

        #region Event Handler functions
        private void Nodes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
        
            ResetRowNodeProperties();

            UpdateDependencies();
        }
        #endregion
    }
}
